package official_account

import (
	wechat_official_account_cache_service "code.gitea.io/gitea/services/wechat/cache"
	wechat_official_account_vo "code.gitea.io/gitea/services/wechat/vo"
	"context"
	XmlUtils "encoding/xml"
	"net/http"
	"strconv"

	wechat_official_account_user_model "code.gitea.io/gitea/models/wechat"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	"github.com/ArtisanCloud/PowerLibs/v3/http/helper"

	"fmt"
	"time"

	"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
	"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/messages"
	models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
	"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
)

// CallbackVerifyMessage
/**
 * 微信服务器验证消息
 * GET /api/wechat/official-account/callback/message
 * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
 */
func CallbackVerifyMessage(responseWriter http.ResponseWriter, request *http.Request) {
	resp, err := setting.Wechat.OfficialAccount.PowerWechat.Server.VerifyURL(request)
	if err != nil {
		log.Error("[X] Failed to verify message in Wechat Official Account callback")
		responseWriter.WriteHeader(resp.StatusCode)
		responseWriter.Write([]byte("Failed to verify message in Wechat Official Account callback (CallbackVerifyMessage): " + err.Error()))
		return
	}

	// 给微信服务器回信
	err = helper.HttpResponseSend(resp, responseWriter)
	if err != nil {
		log.Error("[X] Failed to reply back to Wechat Server (CallbackVerifyMessage): ", err.Error())
		responseWriter.WriteHeader(500)
	}
}

// CallbackNotifyEvents
/**
 * 微信服务器通知事件
 * POST /api/wechat/official-account/callback/message
 * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
 */
func CallbackNotifyEvents(responseWriter http.ResponseWriter, request *http.Request) {

	resp, err := setting.Wechat.OfficialAccount.PowerWechat.Server.Notify(request, func(event contract.EventInterface) interface{} {

		switch event.GetMsgType() {

		case models2.CALLBACK_MSG_TYPE_EVENT:
			//  "event" 类型消息处理
			return callbackMsgEventHandler(request, event)

		case models2.CALLBACK_MSG_TYPE_TEXT:
			// "text" 类型消息处理
			return callbackMsgTextHandler(event)
		}

		return messages.NewText("undefined event type: " + event.GetMsgType())
	})

	if err != nil {
		log.Error("[X] Failed to get notified in Wechat Official Account callback (CallbackNotifyEvents): ", err.Error())
		responseWriter.WriteHeader(resp.StatusCode)
		responseWriter.Write([]byte("Failed to verify message in Wechat Official Account callback" + err.Error()))
	}

	// 给微信服务器回信
	err = helper.HttpResponseSend(resp, responseWriter)
	if err != nil {
		log.Error("[X] Failed to reply back to Wechat Server (CallbackNotifyEvents): ", err.Error())
		responseWriter.WriteHeader(500)
	}
}

func callbackMsgEventHandler(request *http.Request, event contract.EventInterface) *messages.Text {
	ctx := request.Context()

	// 根据不同 event 分别处理
	switch event.GetEvent() {

	case models.CALLBACK_EVENT_SUBSCRIBE:
		// event:subscribe 新用户关注公众号事件
		return eventSubscribeHandler(event)

	case models.CALLBACK_EVENT_SCAN:
		// event:SCAN 老用户扫描二维码事件
		return eventScanHandler(ctx, event)

	case models.CALLBACK_EVENT_UNSUBSCRIBE:
		// event:unsubscribe 掉粉事件处理
		return eventUnsubscribeHandler(event)
	}
	return messages.NewText("undefined event type: " + event.GetChangeType())
}

// "text" 类型消息处理
func callbackMsgTextHandler(event contract.EventInterface) *messages.Text {
	msg := models.MessageText{}
	err := event.ReadMessage(&msg)
	if err != nil {
		log.Error("[X] Failed to handle callback Message TEXT: " + err.Error())
		return messages.NewText("error: " + err.Error())
	}
	// fmt.Dump(msg)
	log.Info("[+] Got message from " + msg.FromUserName + ": " + msg.Content)
	return messages.NewText("收到" + msg.Content)
}

// event:subscribe 新用户关注公众号事件
func eventSubscribeHandler(event contract.EventInterface) *messages.Text {
	msg := models.EventSubscribe{}
	err := event.ReadMessage(&msg)
	if err != nil {
		log.Error("[X] Failed to handle callback Message event:subscribe: " + err.Error())
		return messages.NewText("error: " + err.Error())
	}
	log.Info("[+] event:subscribe User Subscribed: " + msg.FromUserName + ", at Timestamp " + msg.CreateTime)
	return messages.NewText("欢迎新用户 " + msg.FromUserName + " 关注公众号, 请在主页扫码注册账号，或在用户设置页面绑定已有账号")
}

// event:SCAN 类型消息，已关注公众号的老用户扫码
func eventScanHandler(ctx context.Context, event contract.EventInterface) *messages.Text {

	msg := models.EventScanCodePush{}
	err := event.ReadMessage(&msg)
	if err != nil {
		log.Error("[X] Failed to handle callback event SCAN: " + err.Error())
		return messages.NewText("error: " + err.Error())
	}

	var qrScanResponseDigest struct {
		FromUserName string `xml:"FromUserName"`
		Ticket       string `xml:"Ticket"`
		SceneStr     string `xml:"EventKey"`
	}
	err = XmlUtils.Unmarshal([]byte(event.GetContent()), &qrScanResponseDigest)
	if err != nil {
		log.Error("[X] Failed to handle callback event SCAN: " + err.Error())
		return messages.NewText("error: " + err.Error())
	}

	// 准备扫码状态VO对象
	qrStatusVO := wechat_official_account_vo.GetWechatOfficialAccountTempQRStatusVO{
		SceneStr:  qrScanResponseDigest.SceneStr,
		IsScanned: true,
		OpenId:    qrScanResponseDigest.FromUserName,
	}
	// 从微信服务器消息推送中解析扫码人的 OpenID
	//wechatOfficialAccountOpenid := qrScanResponseDigest.FromUserName
	user, err := wechat_official_account_user_model.QueryUserByOpenid(ctx, qrStatusVO.OpenId)

	if err != nil {
		// 未找到 OpenID 对应的 DevStar 用户信息，提示前端导向注册页
		qrStatusVO.IsBinded = false
		qrStatusVOString, err := qrStatusVO.Marshal2JSONString()
		if err == nil {
			// 将扫码人的微信公众号 OpenID 标记为等待注册，等待时间用户注册完成，默认24小时
			//    key:  qrScanResponseDigest.Ticket
			//  value:  JSON 字符串
			//    TTL:  setting.Wechat.OfficialAccount.TempQrExpireSeconds
			wechat_official_account_cache_service.SetWechatOfficialAccountQrTicketWithTTL(
				qrScanResponseDigest.Ticket,
				qrStatusVOString,
				setting.Wechat.OfficialAccount.RegisterationExpireSeconds)
		}
		loginFailedInfo := "登录失败：账号未绑定\n" +
			"\n" +
			"请根据页面提示进行注册，或使用密码登录后绑定微信\n" +
			"\n" +
			fmt.Sprintf("未绑定用户名: %v", qrStatusVO.OpenId)
		return messages.NewText(loginFailedInfo)
	}

	// 找到已绑定用户信息

	log.Info("扫码登录成功:\n" +
		fmt.Sprintf("  扫码人:    `%s` (微信公众号用户识别码openid: %s)\n", user.Name, qrStatusVO.OpenId) +
		fmt.Sprintf("  Ticket:   %s\n", qrScanResponseDigest.Ticket) +
		fmt.Sprintf("  SceneStr: %s\n", qrStatusVO.SceneStr))

	// 将扫码人的微信公众号 OpenID 绑定到微信二维码 Ticket，提示前端导向登录成功页面
	//    key:  qrScanResponseDigest.Ticket
	//  value:  JSON 字符串
	//    TTL:  setting.Wechat.OfficialAccount.TempQrExpireSeconds
	qrStatusVO.IsBinded = true
	qrStatusVO.OpenId = qrScanResponseDigest.FromUserName
	qrVOJsonString, err := qrStatusVO.Marshal2JSONString()
	if err == nil {
		wechat_official_account_cache_service.SetWechatOfficialAccountQrTicketWithTTL(
			qrScanResponseDigest.Ticket,
			qrVOJsonString,
			setting.Wechat.OfficialAccount.TempQrExpireSeconds)
	}

	timestamp, _ := strconv.Atoi(msg.CreateTime)
	beijingDateTimeStr := time.Unix(int64(timestamp), 0).Add(8 * time.Hour).Format("2006-01-02 15:04")
	loginSuccessInfo := "微信扫码登录成功通知\n" +
		"\n" +
		fmt.Sprintf("用户名: %s", user.Name) + "\n" +
		fmt.Sprintf("邮  箱: %s", user.Email) + "\n" +
		fmt.Sprintf("北京时间: %v", beijingDateTimeStr) + "\n\n" +
		fmt.Sprintf("微信公众号用户 OpenID: %v", qrStatusVO.OpenId) + "\n" +
		"\n" +
		"如非本人，请尽快重新绑定微信！"
	log.Info(fmt.Sprintf("[+] event:SCAN 老用户 '%v' 登录, sceneStr: %v, Timestamp: %v", qrStatusVO.OpenId, msg.CreateTime, msg.EventKey))
	return messages.NewText(loginSuccessInfo)
}

// event:unsubscribe 掉粉事件处理
func eventUnsubscribeHandler(event contract.EventInterface) *messages.Text {
	msg := models.EventUnSubscribe{}
	err := event.ReadMessage(&msg)
	if err != nil {
		log.Error("[X] Failed to handle callback Message event:unsubscribe: " + err.Error())
		return messages.NewText("error: " + err.Error())
	}
	log.Info("[+] event:unsubscribe User Unsubscribed: " + msg.ToUserName + ", at Timestamp " + msg.CreateTime)
	return messages.NewText("user unsubscribed: " + msg.FromUserName)
}
